package com.gxar.fusion.authorization.interception;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.gxar.common.entity.Result;
import com.gxar.common.entity.SystemCodeEnum;
import com.gxar.common.entity.login.LoginUserThread;
import com.gxar.common.entity.login.UserProfile;
import com.gxar.fusion.authorization.constant.UserConstant;
import com.gxar.fusion.authorization.model.ResultCodeEnum;
import com.gxar.fusion.authorization.properties.AuthProperties;
import com.gxar.fusion.authorization.properties.PermissionProperties;
import com.gxar.fusion.authorization.properties.SysProperties;
import com.gxar.fusion.authorization.reference.AuthReference;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 登录拦截器
 * 校验用户登录状态,将用户信息放入线程对象
 *
 * @author Linsy
 * @version 1.0
 * @created 2023/10/17
 */
@Component
@Slf4j
public class PermissionInterception implements HandlerInterceptor {

    @Resource
    private AuthReference authReference;
    @Resource
    private AuthProperties authProperties;
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public boolean preHandle(HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        PermissionProperties permissionProperties = authProperties.getPermission();
        if (Objects.nonNull(permissionProperties)) {
            if (StringUtils.isNotBlank(permissionProperties.getAuthPrefix())) {
                // 不是以认证前缀开头的直接放行
                if (!request.getRequestURI().startsWith(permissionProperties.getAuthPrefix())) {
                    return true;
                }
            }
            List<String> noLoginUrl = permissionProperties.getIgnoreUrl();
            // 权限白名单直接放行
            if (Optional.ofNullable(noLoginUrl).orElse(Collections.emptyList()).stream().anyMatch(e -> request.getRequestURI().startsWith(e))) {
                return true;
            }
        }
        UserProfile userProfile = LoginUserThread.get();
        if (Objects.isNull(userProfile)) {
            returnNoLogin(response, null);
            return false;
        }
        String app = userProfile.getApp();
        if (StringUtils.isBlank(app)) {
            returnNoLogin(response, ResultCodeEnum.NO_CONFIG_APP);
            return false;
        }
        List<JSONObject> menuArray = authReference.findMenuByUserId(userProfile.getId(), app);
        String requestMethod = request.getMethod();
        String requestURI = request.getRequestURI();
        if (CollectionUtils.isNotEmpty(menuArray)) {
            for (JSONObject menu : menuArray) {
                if (StringUtils.isNotEmpty(menu.getString("url")) && antPathMatcher.match(menu.getString("url"), requestURI)) {
                    if (StrUtil.isNotEmpty(menu.getString("urlMethod"))) {
                        if (requestMethod.equalsIgnoreCase(menu.getString("urlMethod"))) {
                            return true;
                        }
                    } else {
                        return true;
                    }
                }
            }
        }
        returnNoLogin(response, null);
        return false;
    }

    /**
     * 返回未登录的错误信息
     *
     * @param response ServletResponse
     */
    private void returnNoLogin(HttpServletResponse response, ResultCodeEnum resultCodeEnum) throws IOException {
        try (ServletOutputStream outputStream = response.getOutputStream()) {
            // 设置返回401 和响应编码
            response.setStatus(401);
            response.setContentType("Application/json;charset=utf-8");
            // 构造返回响应体
            Result<Object> result = Result.error(Objects.nonNull(resultCodeEnum) ? resultCodeEnum : ResultCodeEnum.UN_PERMISSION);
            String resultString = JSON.toJSONString(result);
            outputStream.write(resultString.getBytes(StandardCharsets.UTF_8));
        }
    }

    /**
     * preHandle 返回true时清理对象
     *
     * @param request  请求
     * @param response 响应
     * @param handler  handler
     * @param ex       异常
     */
    @Override
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, @Nullable Exception ex) {
        // 释放用户线程对象
        LoginUserThread.remove();
    }

}